#ifndef BOUNDING_VOLUME_HPP
#define BOUNDING_VOLUME_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Color.hpp>
#include <SFML/System/Vector2.hpp>

#include <SmegTools/Config.h>
#include <SmegTools/Pattern/Clonable.hpp>
#include <SmegTools/Debug/DebugNew.hpp>

////////////////////////////////////////////////////////////
/// Defines for the Cohen-Sutherland algorithm
/// http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
////////////////////////////////////////////////////////////
#define INSIDE  0   // 0000
#define LEFT    1   // 0001
#define RIGHT   2   // 0010
#define BOTTOM  4   // 0100
#define TOP     8   // 1000

/*
----------------------
| 1001 | 1000 | 1010 |
----------------------
| 0001 | 0000 | 0010 |
----------------------
| 0101 | 0100 | 0110 |
----------------------

*/

#ifdef _DEBUG
namespace sf {
    class RenderWindow;
}
#endif

namespace smeg {
    namespace math {
        
class BoundingBox;
class Circle;
class Line;

class BoundingVolume : public tools::Clonable< BoundingVolume >
{
public:
    enum eBoundingVolume {
        eNone           = 0,
        eBoundingBox    = 1 << 0,
        eCircle         = 1 << 1,
        eLine           = 1 << 2,
        eAny            = 0xF
    };

    ////////////////////////////////////////////////////////////
    /// Default Constructor
    ///
    /// \param _Center : Center of the BoundingBox
    /// \param _Size :  Size of the BoundingBox
    ///
    ////////////////////////////////////////////////////////////
                        BoundingVolume( eBoundingVolume _eType,
                                        const sf::Vector2f& _Center = sf::Vector2f(),
                                        const sf::Vector2f& _Size = sf::Vector2f() );
    ////////////////////////////////////////////////////////////
    /// Copy Constructor
    ///
    /// \param _Copy
    ///
    ////////////////////////////////////////////////////////////
                        BoundingVolume( const BoundingVolume& _Copy );
    ////////////////////////////////////////////////////////////
    /// Destructor
    ///
    ////////////////////////////////////////////////////////////
    virtual             ~BoundingVolume();
    
    ////////////////////////////////////////////////////////////
    /// Setup the Bounding Volume
    ///
    /// \param _Center : Center of the Bounding Volume
    /// \param _Size :  Size of the Bounding Volume
    ///
    ////////////////////////////////////////////////////////////
    virtual void        Setup( const sf::Vector2f& _center, const sf::Vector2f& _size );
    
    ////////////////////////////////////////////////////////////
    /// Does the Bounding Volume overlaps with another BoundingVolume
    ///
    /// \param _pOther : Other BoundingVolume to test
    ///
    /// \return Whether the _Other BoundingVolume overlaps with this Bounding Volume
    ///
    ////////////////////////////////////////////////////////////
    bool                OverlapsUnknownBBPtr( const BoundingVolume* _pOther ) const;
    
    ////////////////////////////////////////////////////////////
    /// Does the Bounding Volume overlaps with a Bounding Volume
    ///
    /// \param _Other : Other Volume to test
    ///
    /// \return Whether the _Other Bounding Volume overlaps with this Bounding Volume
    ///
    ////////////////////////////////////////////////////////////
    virtual bool        Overlaps( const BoundingBox& _Other ) const   = 0;
    virtual bool        Overlaps( const Line& _Other ) const          = 0;
    virtual bool        Overlaps( const Circle& _Other ) const        = 0;
    
    ////////////////////////////////////////////////////////////
    /// Does the Bounding Volume contains another BoundingVolume
    ///
    /// \param _pOther : Other BoundingVolume to test
    ///
    /// \return Whether this Bounding Volume contains the _Other BoundingVolume
    ///
    ////////////////////////////////////////////////////////////
    bool                ContainsUnknownBBPtr( const BoundingVolume* _pOther ) const;
    
    ////////////////////////////////////////////////////////////
    /// Does the Bounding Volume contains a Bounding Volume
    ///
    /// \param _Other : Other Volume to test
    ///
    /// \return Whether this contains the _Other Bounding Volume
    ///
    ////////////////////////////////////////////////////////////
    virtual bool        Contains( const BoundingBox& _Other ) const = 0;
    virtual bool        Contains( const Line& _Other ) const        = 0;
    virtual bool        Contains( const Circle& _Other ) const      = 0;

    ////////////////////////////////////////////////////////////
    /// Does the Bounding Volume contains a point
    ///
    /// \param _Point : Point to test
    ///
    /// \return Whether this contains the point
    ///
    ////////////////////////////////////////////////////////////
    virtual bool        Contains( const sf::Vector2f& _point ) const = 0;
    
    /*********************** Accessors ************************/
    virtual void        SetCenter( float _fX, float _fY );
    virtual void        MoveCenter( float _fX, float _fY );
    const sf::Vector2f& GetCenter() const;
    
    virtual void        SetSize( float _fX, float _fY );
    const sf::Vector2f& GetSize() const;
    const sf::Vector2f& GetHalfSize() const;

    eBoundingVolume     GetType() const;

    virtual float       GetPseudoRadius() const = 0;

protected:
    ////////////////////////////////////////////////////////////
    /// When 2 Bounding Volumes overlaps,
    /// whether its VolumeA and VolumeB or VolumeB and VolumeA
    /// the result is the same, these static functions are here to avoid code duplication
    ///
    /// \return Whether the two volume overlaps with each other
    ///
    ////////////////////////////////////////////////////////////
    static bool         Overlaps( const BoundingBox& _vA, const Circle& _vB );
    static bool         Overlaps( const BoundingBox& _vA, const Line& _vB );
    static bool         Overlaps( const Circle& _vA, const Line& _vB );

    /******************* Protected members ********************/
    sf::Vector2f        m_Center;
    sf::Vector2f        m_Size;
    sf::Vector2f        m_HalfSize;

    eBoundingVolume     m_eType;

#ifdef _DEBUG
public:
    virtual void        DebugRender( sf::RenderWindow* _pRenderWindow, const sf::Color& _Color = sf::Color::White, bool _bFull = false ) const = 0;
#endif // _DEBUG
};

/************************** Inline ************************/
inline const sf::Vector2f& BoundingVolume::GetCenter() const {
    return m_Center;
}

inline const sf::Vector2f& BoundingVolume::GetSize() const {
    return m_Size;
}

inline const sf::Vector2f& BoundingVolume::GetHalfSize() const {
    return m_HalfSize;
}

inline BoundingVolume::eBoundingVolume BoundingVolume::GetType() const {
    return m_eType;
}

    } // namespace math
} // namespace smeg

#include <SmegTools/Debug/DebugNewOff.hpp>

#endif // BOUNDING_VOLUME_HPP
